﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using parkMonitor.entity;
using parkMonitor.model;
using parkMonitor.server;
using System.Configuration;
using parkMonitor.tools;
using parkMonitor.server.uiLogServer;

namespace parkMonitor.server.CoreThread
{
    /// <summary>
    /// 队列线程，处理号牌与停取车指令业务逻辑
    /// </summary>
    public class QueuingThread : IEquipments
    {
        Queue<NumberMachineNode> LicenseQueue = new Queue<NumberMachineNode>();
        Queue<Command> StoreCmdQueue = new Queue<Command>();
        Queue<Command> ValidStoreCmdQueue = new Queue<Command>();
        Queue<Command> FetchCmdQueue = new Queue<Command>();
        BlockingQueue ExecutableCmdQueue = new BlockingQueue();
        int fetchStoreRatio, licenseTime, userTime;
        bool isClosing = false;
        IEquipments webHandle = null;
        IEquipments numMachineHandle = null;
        NumberMachineNode license;
        MessageUTF8 webMsg;

        /// <summary>
        /// 队列构造函数
        /// </summary>
        public QueuingThread()
        {
            webHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.Web);
            numMachineHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.NumMachine);
            try
            {
                fetchStoreRatio = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_store_ratio"));
                licenseTime = Int32.Parse(ConfigurationManager.AppSettings.Get("licenseTime"));
                userTime = Int32.Parse(ConfigurationManager.AppSettings.Get("userTime"));
            }
            catch (Exception) { fetchStoreRatio = 3; UILogServer.ins.error("未找到停取车比例与计时参数"); }
        }

        /// <summary>
        /// 从web线程获得用户指令
        /// </summary>
        private void ReceiveFromWebThread()
        {
            while (!isClosing)
            {
                Command cmd = null;
                //接收web消息
                if (webHandle != null)
                {
                    try
                    {
                        webMsg = (MessageUTF8)((MessageUTF8)webHandle.GetMessage()).Clone();
                    }
                    catch (Exception) { UILogServer.ins.error("用户指令异常"); }
                }
                //get useful data from webMsg and assign it to output data structure
                if (webMsg != null)
                {
                    cmd = new Command();
                    cmd.commandType = webMsg.cmd;
                    cmd.LicenseNum = webMsg.context;
                    cmd.userID = webMsg.sender;
                    cmd.garageID = webMsg.garageID;
                    cmd.parkingRecordsID = webMsg.parkingRecordsID;
                    cmd.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    webMsg = null;
                }
                //input user command
                if (cmd != null)
                {
                    if (cmd.commandType.Equals('f'))
                    {
                        lock (FetchCmdQueue)
                        {
                            FetchCmdQueue.Enqueue((Command)cmd.Clone());
                        }
                        UILogServer.ins.info("收到web取车指令：" + cmd.LicenseNum);
                    }
                    //停车先遍历
                    else if (cmd.commandType.Equals('s'))
                    {
                        UserCmdMatch(cmd);
                        UILogServer.ins.info("收到web停车指令：" + cmd.LicenseNum);
                    }
                    else
                    {
                        Console.WriteLine("invalid user command.");
                        UILogServer.ins.error("从web收到无法识别的用户指令,并非停取车");
                    }
                }
                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// 队列线程主业务逻辑
        /// </summary>
        private void Run()
        {
            NumberMachineNode temp = null;
            while (!isClosing)
            {
                lock (LicenseQueue)
                {
                    //本地号牌已使用，则接收新的号牌消息
                    if (numMachineHandle != null && !NumMachineNodeValidation(license))
                    {
                        license = ((NumberMachineMessage)numMachineHandle.GetMessage()).aNode;
                        //无重复则匹配指令，匹配失败则入号牌队列，并将号牌机线程获得的Node中ip标记为已使用
                        if (NumMachineNodeValidation(license) && !LicenseQueue.Contains(license))
                        {
                            UILogServer.ins.info("已扫描到号牌：" + license.LicenseNum + "，匹配中");
                            Console.WriteLine("已扫描到号牌：" + license.LicenseNum + "，匹配中");
                            //先匹配指令
                            NumMatch(ref license);
                        }
                    }

                    //将已被使用的号牌从号牌队列排除
                    for (int i = 0; i < LicenseQueue.Count; i++)
                    {
                        temp = LicenseQueue.Dequeue();
                        if (temp != null && temp.ip != "used")
                        {
                            LicenseQueue.Enqueue(temp);
                        }
                    }
                }

                //队列业务逻辑
                //UserCmdMatch();
                Scheduling();
                lock (StoreCmdQueue)
                {
                    UserCmdTimeOut();
                }
                lock (LicenseQueue)
                {
                    NumTimeOut();
                }
                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// 号牌节点有效性验证
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool NumMachineNodeValidation(NumberMachineNode node)
        {
            return (node != null && node.ip != null && node.ip != "" && node.ip != "used") ? true : false;
        }

        /// <summary>
        /// 停车指令超时处理
        /// </summary>
        private void UserCmdTimeOut()
        {
            Command cmd = null;
            for (int i = 0; i < StoreCmdQueue.Count; i++)
            {
                cmd = StoreCmdQueue.Dequeue();

                //指令超时处理
                TimeSpan userTimeSpan = DateTime.Now - DateTime.Parse(cmd.TimeRecord);
                //Console.WriteLine("指令已扫描时间：" + userTimeSpan.TotalSeconds);
                if (userTimeSpan.TotalMinutes >= userTime)
                {
                    UILogServer.ins.error("异常：用户指令 "+ cmd.LicenseNum+ " 已存在超过" + userTimeSpan.TotalMinutes + "分钟 ,强制出队");
                    //cmd.LicenseNum = "";
                    cmd.commandType = 'e';
                    lock (ValidStoreCmdQueue)
                    {
                        ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                    if (StoreCmdQueue.Count != 0)
                        cmd = StoreCmdQueue.Dequeue();
                    else
                        break;
                }
                //未超时则归队
                else
                {
                    StoreCmdQueue.Enqueue((Command)cmd.Clone());
                }
            }
        }

        /// <summary>
        /// 号牌超时处理
        /// </summary>
        private void NumTimeOut()
        {
            for (int i = 0; i < LicenseQueue.Count; i++)
            {
                NumberMachineNode node = LicenseQueue.Dequeue();
                if (NumMachineNodeValidation(node))
                {
                    //计算时间差，超过5分钟自动出队（？处理方式待讨论），并将该Node标记为已使用，等待统一处理
                    TimeSpan licTimeSpan = DateTime.Now - DateTime.Parse(node.TimeRecord);
                    if (licTimeSpan.TotalMinutes >= licenseTime)
                    {
                        //异常号牌不再入队，而是界面提示管理员,并让号牌机线程
                        UILogServer.ins.error("异常：号牌 " + node.LicenseNum + " 已扫描超过" + String.Format("{0:F1}",licTimeSpan.TotalMinutes) + "分钟 ,强制出队");
                        //停车完成，将相应车牌复位
                        NumberMachineMessage numberMachineMessage = new NumberMachineMessage();
                        numberMachineMessage.aNode = new NumberMachineNode();
                        numberMachineMessage.aNode.ip = "";
                        numberMachineMessage.aNode.LicenseNum = node.LicenseNum;
                        numMachineHandle.SetMessage(numberMachineMessage);
                        node.ip = "used";
                    }
                }
                LicenseQueue.Enqueue(node);
            }
        }

        /// <summary>
        /// 单用户指令匹配号牌队列
        /// </summary>
        private void UserCmdMatch(Command cmd)
        {
            lock (StoreCmdQueue)
            {
                NumberMachineNode num = null;
                bool matched = false;
                num = new NumberMachineNode();
                lock (LicenseQueue)
                {
                    for (int i = 0; i < LicenseQueue.Count; i++)
                    {
                        num = LicenseQueue.Dequeue();
                        //匹配成功
                        if (NumMachineNodeValidation(num) && cmd.LicenseNum.Equals(num.LicenseNum))
                        {
                            cmd.TimeRecord = num.TimeRecord;
                            cmd.ip = num.ip;
                            cmd.id = num.id;
                            ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                            matched = true;
                            break;
                        }
                        else
                        {
                            LicenseQueue.Enqueue((NumberMachineNode)num.Clone());
                        }
                    }
                }
                if (!matched && !StoreCmdQueue.Contains(cmd))
                {
                    StoreCmdQueue.Enqueue((Command)cmd.Clone());
                }
            }
        }

        /// <summary>
        /// 单号牌匹配用户指令队列
        /// </summary>
        private void NumMatch(ref NumberMachineNode license)
        {
            Command cmd = new Command();
            bool matched = false;
            lock (StoreCmdQueue)
            {
                for (int i = 0; NumMachineNodeValidation(license) && i < StoreCmdQueue.Count; i++)
                {
                    cmd = StoreCmdQueue.Dequeue();
                    //匹配成功
                    if (license.LicenseNum.Equals(cmd.LicenseNum))
                    {
                        cmd.TimeRecord = license.TimeRecord;
                        cmd.ip = license.ip;
                        cmd.id = license.id;
                        ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                        matched = true;
                        break;
                    }
                    else
                    {
                        StoreCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                }
            }
            lock (LicenseQueue)
            {
                if (!matched && !LicenseQueue.Contains(license))
                {
                    LicenseQueue.Enqueue((NumberMachineNode)license.Clone());
                }
            }
            license.ip = "used";
        }

        /// <summary>
        /// 停取车调度，控制停取车比例，可多取一停，也可一取一停
        /// </summary>
        private void Scheduling()
        {
            //scheduling algorithm
            int fetch = fetchStoreRatio;
            Command cmd = null;
            lock (FetchCmdQueue)
            {
                while (FetchCmdQueue.Count != 0 && fetch-- > 0)
                {
                    cmd = FetchCmdQueue.Dequeue();
                    ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
                    UILogServer.ins.info("任务：取出号牌为 " + cmd.LicenseNum + " 的车辆，开始排队");
                }
            }
            lock (ValidStoreCmdQueue)
            {
                if (ValidStoreCmdQueue.Count != 0)
                {
                    cmd = ValidStoreCmdQueue.Dequeue();
                    ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
                    UILogServer.ins.info("任务：存入号牌为 " + cmd.LicenseNum + " 的车辆，开始排队");
                }
            }
        }

        /// <summary>
        /// 获取一个停取车指令
        /// </summary>
        public AbstractMessage GetMessage()
        {
            lock (ExecutableCmdQueue)
            {
                return (Command)ExecutableCmdQueue.Dequeue();
            }
        }

        /// <summary>
        /// 功能一：由核心线程调用，传入已存车完成需要标记的号牌
        /// 功能二：添加手动停取车指令
        /// </summary>
        public void SetMessage(AbstractMessage message)
        {
            //号牌机重置消息
            if (message.GetType().Equals(typeof(NumberMachineMessage)))
            {
                if (numMachineHandle != null)
                {
                    numMachineHandle.SetMessage(message);
                }
            }
            //手动停取车指令
            else if (message.GetType().Equals(typeof(Command)))
            {
                Command command = (Command)message;
                //command.garageID = 0;
                //command.parkingRecordsID = 0;
                command.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                //input user command
                if (command != null)
                {
                    if (command.returnedCount > 0)
                    {
                        if (command.commandType.Equals('s'))
                        {
                            lock (ValidStoreCmdQueue)
                            {
                                ValidStoreCmdQueue.Enqueue((Command)command.Clone());
                            }
                            UILogServer.ins.info("收到退回的停车指令：" + command.LicenseNum);
                        }
                        else if(command.commandType.Equals('f')){
                            lock (FetchCmdQueue)
                            {
                                FetchCmdQueue.Enqueue((Command)command.Clone());
                            }
                            UILogServer.ins.info("收到退回的取车指令：" + command.LicenseNum);
                        }
                    }
                    else
                    {
                        if (command.commandType.Equals('f'))
                        {
                            lock (FetchCmdQueue)
                            {
                                FetchCmdQueue.Enqueue((Command)command.Clone());
                            }
                            UILogServer.ins.info("收到手动的取车指令：" + command.LicenseNum);
                        }
                        else if (command.commandType.Equals('s'))
                        {
                            UserCmdMatch(command);
                            UILogServer.ins.info("收到手动的停车指令：" + command.LicenseNum);
                        }
                        else
                        {
                            Console.WriteLine("invalid user command.");
                            UILogServer.ins.error("收到无法识别的手动用户指令");
                        }
                    }
                }
            }
        }

        public void Start()
        {
            license = null;
            webMsg = null;
            Task.Factory.StartNew(() =>
            {
                ReceiveFromWebThread();
            });
            Task.Factory.StartNew(() =>
            {
                Run();
            });
        }

        public void Stop()
        {
            isClosing = true;
        }
    }
}
